package top.hkyzf.util.movement;

/**
 * description: 旋转
 * 欧拉角是表达旋转的最简单的一种方式，pitch, yaw, roll。
 * pitch 是围绕 X 轴旋转，也叫做俯仰角。
 * 当 X 轴的正半轴位于过坐标原点的水平面之上（抬头）时，俯仰角为正，否则为负。
 * yaw 是围绕Y轴旋转，也叫偏航角。
 * 即机头右偏航为正，反之为负。游戏中向右侧转身为正，反之为负
 * roll是围绕Z轴旋转，也叫翻滚角。
 * 机体向右滚为正，反之为负。
 * @author zhufeng
 * @date 2022/8/22 10:53
 * @version V1.0
 */
public final class Rotation {

    /**
     * 转身角度，player 中的 yRot
     */
    private final float yaw;

    /**
     * 抬头角度，player 中的 xRot
     */
    private final float pitch;

    public Rotation(float yaw, float pitch) {
        this.yaw = yaw;
        this.pitch = pitch;
        if (Float.isInfinite(yaw) || Float.isNaN(yaw) || Float.isInfinite(pitch) || Float.isNaN(pitch)) {
            throw new IllegalStateException(yaw + " " + pitch);
        }
    }

    public float getYaw() {
        return this.yaw;
    }

    public float getPitch() {
        return this.pitch;
    }

    /**
     * 相加的操作
     * @param other 另一个旋转
     * @return 两个旋转相加后的结果
     */
    public Rotation add(Rotation other) {
        return new Rotation(
                this.yaw + other.yaw,
                this.pitch + other.pitch
        );
    }

    /**
     * 相减的操作
     * @param other 另一个旋转
     * @return 两个旋转相减后的结果
     */
    public Rotation subtract(Rotation other) {
        return new Rotation(
                this.yaw - other.yaw,
                this.pitch - other.pitch
        );
    }

    /**
     * 创建一个原旋转类 pitch 合法化后的副本
     * @return pitch 合法化的旋转类
     */
    public Rotation clamp() {
        return new Rotation(
                this.yaw,
                clampPitch(this.pitch)
        );
    }

    /**
     * 创建一个原旋转类 yaw 合法化后的副本
     * @return yaw 合法化的旋转类
     */
    public Rotation normalize() {
        return new Rotation(
                normalizeYaw(this.yaw),
                this.pitch
        );
    }

    /**
     * 创建一个原旋转类 yaw 和 pitch 合法化后的副本
     * @return yaw 和 pitch 合法化的旋转类
     */
    public Rotation normalizeAndClamp() {
        return new Rotation(
                normalizeYaw(this.yaw),
                clampPitch(this.pitch)
        );
    }

    /**
     * 判断两个旋转角度相同，非常接近
     * @param other 另一个旋转
     * @return 是否相同或非常接近
     */
    public boolean isReallyCloseTo(Rotation other) {
        return yawIsReallyClose(other) && Math.abs(this.pitch - other.pitch) < 0.01;
    }

    /**
     * yaw 值非常接近
     * @param other 另一个旋转
     * @return 是否相同或非常接近
     */
    public boolean yawIsReallyClose(Rotation other) {
        float yawDiff = Math.abs(normalizeYaw(yaw) - normalizeYaw(other.yaw));
        return (yawDiff < 0.01 || yawDiff > 359.99);
    }

    /**
     * 将 pitch 合法化，在 [-90, 90] 之间
     * @param pitch 输入的抬头角度
     * @return 规范化后的抬头角度
     */
    public static float clampPitch(float pitch) {
        return Math.max(-90, Math.min(90, pitch));
    }

    /**
     * 将 yaw 值合法化，在 [-180, 180] 之间
     * @param yaw 输入的转身角度
     * @return 规范化后的转身角度
     */
    public static float normalizeYaw(float yaw) {
        float newYaw = yaw % 360F;
        if (newYaw < -180F) {
            newYaw += 360F;
        }
        if (newYaw > 180F) {
            newYaw -= 360F;
        }
        return newYaw;
    }

    @Override
    public String toString() {
        return "Yaw: " + yaw + ", Pitch: " + pitch;
    }
}
